/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
/*
* Created on Oct 29, 2006
* @author Fabio
*/
package org.python.pydev.navigator.actions;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.navigator.CommonViewer;
import org.eclipse.ui.navigator.ILinkHelper;
import org.eclipse.ui.part.FileEditorInput;
import org.python.pydev.core.IInterpreterInfo;
import org.python.pydev.core.log.Log;
import org.python.pydev.core.structure.TreeNode;
import org.python.pydev.editor.actions.PyAction;
import org.python.pydev.navigator.InterpreterInfoTreeNodeRoot;
import org.python.pydev.navigator.PythonpathTreeNode;
import org.python.pydev.navigator.elements.IWrappedResource;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class PythonLinkHelper implements ILinkHelper {
private WeakReference<CommonViewer> commonViewer;
public void setCommonViewer(CommonViewer commonViewer) {
this.commonViewer = new WeakReference<CommonViewer>(commonViewer);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.navigator.ILinkHelper#findSelection(org.eclipse.ui.IEditorInput)
*/
public IStructuredSelection findSelection(IEditorInput anInput) {
if (anInput instanceof IFileEditorInput) {
return new StructuredSelection(((IFileEditorInput) anInput).getFile());
}
if (anInput instanceof IAdaptable) {
//handles org.eclipse.compare.CompareEditorInput without a specific reference to it
Object adapter = anInput.getAdapter(IFile.class);
if (adapter != null) {
return new StructuredSelection(adapter);
}
}
return StructuredSelection.EMPTY;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.navigator.ILinkHelper#activateEditor(org.eclipse.ui.IWorkbenchPage, org.eclipse.jface.viewers.IStructuredSelection)
*/
public void activateEditor(IWorkbenchPage aPage, IStructuredSelection aSelection) {
if (aSelection == null || aSelection.isEmpty()) {
return;
}
Object firstElement = aSelection.getFirstElement();
//if it is a python element, let's first get the actual object for finding the editor
if (firstElement instanceof IWrappedResource) {
IWrappedResource resource = (IWrappedResource) firstElement;
firstElement = resource.getActualObject();
}
//and now, if it is really a file...
if (firstElement instanceof IFile) {
//ok, let's check if the active editor is already the selection, because although the findEditor(editorInput) method
//may return an editor for the correct file, we may have multiple editors for the same file, and if the current
//editor is already correct, we don't want to change it
//@see bug: https://sourceforge.net/tracker/?func=detail&atid=577329&aid=2037682&group_id=85796
IEditorPart activeEditor = aPage.getActiveEditor();
if (activeEditor != null) {
IEditorInput editorInput = activeEditor.getEditorInput();
IFile currFile = (IFile) editorInput.getAdapter(IFile.class);
if (currFile != null && currFile.equals(firstElement)) {
return; //the current editor is already the active editor.
}
}
//if we got here, the active editor is not a match, so, let's find one and show it.
IEditorPart editor = null;
IEditorInput fileInput = new FileEditorInput((IFile) firstElement);
if ((editor = aPage.findEditor(fileInput)) != null) {
aPage.bringToTop(editor);
}
}
}
/**
* Here we'll try to make a show -> in for an external file. The idea is that we'll traverse the
* available InterpreterInfoTreeNodeRoot's found until we find a match.
*
* Some things need to be taken care of thought:
*
* 1. The same interpreter may appear multiple times, so, if we pass one interpreter once, we should not try to
* traverse any other place where the same interpreter is configured.
*
* 2. As an interpreter may appear multiple times, we have to use some heuristic in order to decide which one will
* be searched first. This is done through:
*
* - looking for the current selection (i.e.: try to get the file in the same project in an existing selection)
* - looking at opened editors (i.e.: try to get the file in the same project of an existing editor)
*
* if that fails, we should go through what's visible in the package explorer and if that still fails, maybe
* show an error to the user.
*
* 3. We may need to look into zip files too.
*/
public IStructuredSelection findExternalFileSelection(File f) {
if (this.commonViewer == null) {
return null;
}
CommonViewer commonViewer = this.commonViewer.get();
if (commonViewer == null) {
return null;
}
ISelection treeSelection = commonViewer.getSelection();
Set<IInterpreterInfo> infosSearched = new HashSet<IInterpreterInfo>();
IContentProvider contentProvider = commonViewer.getContentProvider();
ITreeContentProvider treeContentProvider;
if (contentProvider instanceof ITreeContentProvider) {
treeContentProvider = (ITreeContentProvider) contentProvider;
} else {
Log.log("On tryToRevealExternalFile, the common viewer content provider is not an ITreeContentProvider. Found: "
+ contentProvider);
return null;
}
//Step 1: look into a selection
if (treeSelection instanceof IStructuredSelection && !treeSelection.isEmpty()) {
IStructuredSelection structuredSelection = (IStructuredSelection) treeSelection;
Iterator it = structuredSelection.iterator();
while (it.hasNext()) {
Object next = it.next();
IStructuredSelection sel = findExternalFileSelectionGivenTreeSelection(f, commonViewer,
treeContentProvider, infosSearched, next);
if (sel != null && !sel.isEmpty()) {
return sel;
}
}
}
//Step 2: look into what's expanded in the package explorer
Object[] expandedElements = commonViewer.getVisibleExpandedElements();
for (Object expandedElement : expandedElements) {
IStructuredSelection sel = findExternalFileSelectionGivenTreeSelection(f, commonViewer,
treeContentProvider, infosSearched, expandedElement);
if (sel != null && !sel.isEmpty()) {
return sel;
}
}
//Step 3: look into existing editors
Set<IFile> openFiles = PyAction.getOpenFiles();
for (IFile iFile : openFiles) {
IStructuredSelection sel = findExternalFileSelectionGivenTreeSelection(f, commonViewer,
treeContentProvider, infosSearched, iFile);
if (sel != null && !sel.isEmpty()) {
return sel;
}
}
//Step 4: look into what's available in the package explorer
Object input = commonViewer.getInput();
for (Object child : treeContentProvider.getChildren(input)) {
IStructuredSelection sel = findExternalFileSelectionGivenTreeSelection(f, commonViewer,
treeContentProvider, infosSearched, child);
if (sel != null && !sel.isEmpty()) {
return sel;
}
}
//If all failed, just return null!
return null;
}
private IStructuredSelection findExternalFileSelectionGivenTreeSelection(File f, CommonViewer commonViewer,
ITreeContentProvider treeContentProvider, Set<IInterpreterInfo> infosSearched, Object next) {
if (next instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) next;
IResource resource = (IResource) adaptable.getAdapter(IResource.class);
if (resource != null) {
IProject project = resource.getProject();
if (project != null) {
Object[] children = treeContentProvider.getChildren(project);
for (Object object : children) {
if (object instanceof InterpreterInfoTreeNodeRoot) {
IStructuredSelection sel = findMatchInTreeNodeRoot(f, commonViewer,
(InterpreterInfoTreeNodeRoot) object, infosSearched);
if (sel != null) {
return sel;
}
}
}
return null;
}
}
//Keep on going to try to find a parent that'll adapt to IResource...
} else if (next instanceof TreeNode) {
TreeNode treeNode = (TreeNode) next;
while (true) {
if (treeNode instanceof InterpreterInfoTreeNodeRoot) {
IStructuredSelection sel = findMatchInTreeNodeRoot(f, commonViewer,
(InterpreterInfoTreeNodeRoot) treeNode, infosSearched);
if (sel != null) {
return sel;
}
return null;
}
Object parent = treeNode.getParent();
if (parent instanceof TreeNode) {
treeNode = (TreeNode) parent;
} else {
break;
}
}
//Couldn't find a proper InterpreterInfoTreeNodeRoot already having a TreeNode? Let's log it, as a TreeNode
//should always map to an InterpreterInfoTreeNodeRoot.
Log.log("Couldn't find a proper InterpreterInfoTreeNodeRoot already having TreeNode: " + next);
return null;
}
//Some unexpected type... let's get its parent until we find one expected (or just end up trying if we get to the root).
Object parent = next;
int i = 10000;
//just playing safe to make sure we won't get into a recursion (the tree should never group up to 10000 levels,
//so, this is likely a problem in the content provider).
while (i > 0) {
i--;
if (i == 0) {
Log.log("Found a recursion for the element: " + next
+ " when searching parents. Please report this a a bug!");
}
if (parent == null || parent instanceof IWorkspaceRoot || parent instanceof IWorkingSet) {
break;
}
if (parent instanceof TreeNode) {
return findExternalFileSelectionGivenTreeSelection(f, commonViewer, treeContentProvider, infosSearched,
parent);
} else if (parent instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) parent;
IResource resource = (IResource) adaptable.getAdapter(IResource.class);
if (resource != null) {
IProject project = resource.getProject();
if (project != null) {
return findExternalFileSelectionGivenTreeSelection(f, commonViewer, treeContentProvider,
infosSearched, project);
}
}
}
parent = treeContentProvider.getParent(parent);
}
return null;
}
/**
* Tries to find a match for the element in the given root passed. If found returns true.
*
* @param infosSearched: a memo to know which infos were already searched to prevent searching many times in
* the same place.
*/
private IStructuredSelection findMatchInTreeNodeRoot(File element, CommonViewer commonViewer,
InterpreterInfoTreeNodeRoot treeNodeRoot, Set<IInterpreterInfo> infosSearched) {
if (infosSearched.contains(treeNodeRoot.interpreterInfo)) {
return null;
}
infosSearched.add(treeNodeRoot.interpreterInfo);
List<TreeNode> nodesOrderedForFileSearch = treeNodeRoot.getNodesOrderedForFileSearch();
for (TreeNode node : nodesOrderedForFileSearch) {
PythonpathTreeNode match = findMatch(node, element);
if (match != null) {
return new StructuredSelection(match);
}
}
return null;
}
/**
* Recursively iterates a tree node structure from parent -> children to find a match for the given element.
* The match is returned if found (null is returned if not found).
*/
private PythonpathTreeNode findMatch(TreeNode treeNode, Object element) {
if (treeNode instanceof PythonpathTreeNode) {
PythonpathTreeNode pythonpathTreeNode = (PythonpathTreeNode) treeNode;
if (element.equals(pythonpathTreeNode.file)) {
return pythonpathTreeNode;
}
}
List<TreeNode> children = treeNode.getChildren();
for (TreeNode object : children) {
PythonpathTreeNode m = findMatch(object, element);
if (m != null) {
return m;
}
}
return null;
}
}